package io.itit.ecp.admin.provide.action.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import com.baomidou.mybatisplus.core.metadata.IPage;
import icu.mhb.mybatisplus.plugln.core.JoinLambdaWrapper;
import icu.mhb.mybatisplus.plugln.extend.Joins;
import io.itit.ecp.admin.provide.action.req.CarbonSinkDataQueryReq;
import io.itit.ecp.admin.provide.action.req.CarbonSinkDataReq;
import io.itit.ecp.admin.provide.action.resp.CarbonSinkDataResp;
import io.itit.ecp.admin.provide.filter.ICarbonSinkDataFilter;
import io.itit.ecp.admin.provide.helper.CarbonSinkFactorHelper;
import io.itit.ecp.admin.provide.renderer.CarbonSinkDataRenderer;
import io.itit.ecp.admin.server.entity.*;
import io.itit.ecp.admin.server.service.ICarbonSinkDataService;
import io.itit.grass.common.exception.GrassException;
import io.itit.grass.common.provide.action.req.BaseBatchIdReq;
import io.itit.grass.common.provide.action.req.BaseIdReq;
import io.itit.grass.common.provide.action.resp.ResultResp;
import io.itit.grass.common.provide.action.validation.*;
import io.itit.grass.common.server.utils.ConvertUtils;
import io.itit.grass.estelle.starter.core.ExcelXSSFUtil;
import io.itit.grass.estelle.starter.parse.ExcelParseManage;
import io.itit.grass.estelle.starter.util.JsonUtil;
import io.itit.grass.web.wall.utils.PageUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 *
 * @Author：Roy
 * @Description：碳汇数据控制类
 * @Date：Create by 2023-12-01 10:45:08
 */
@Api(tags = "碳汇数据")
@RestController
@RequestMapping("/ecp-admin/carbonSinkData")
public class CarbonSinkDataController {

    @Resource
    private ICarbonSinkDataService carbonSinkDataService;

    @ApiOperation("分页查询")
    @PostMapping("queryPage")
    @RequiresPermissions("ecp-admin:carbonSinkData:search")
    public ResultResp<IPage<CarbonSinkDataResp>> queryPage(@RequestBody @Validated(IQueryPage.class) CarbonSinkDataQueryReq req) {
        return ResultResp.success(buildQW(req).joinPage(PageUtils.getPage(req, CarbonSinkDataResp.class), CarbonSinkDataResp.class));
    }

    @ApiOperation("列表查询")
    @PostMapping("queryList")
    @RequiresPermissions(value = {"ecp-admin:carbonSinkData:search", "ecp-admin:activityCarbonEmission:add",
            "ecp-admin:activityCarbonEmission:update"}, logical = Logical.OR)
    public ResultResp<List<CarbonSinkDataResp>> queryList(@RequestBody CarbonSinkDataQueryReq req) {
        return ResultResp.success(buildQW2(req).joinList(CarbonSinkDataResp.class));
    }

    private static JoinLambdaWrapper<CarbonSinkDataEntity> buildQW2(CarbonSinkDataQueryReq req) {
        return Joins.of(CarbonSinkDataEntity.class)
                .selectAll()
                .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, CarbonSinkDataEntity::getSceneProjectId)
                .select(SceneProjectEntity::getProjectName)
                .end()
                .leftJoin(SceneEdificeEntity.class, SceneEdificeEntity::getId, CarbonSinkDataEntity::getSceneEdificeId)
                .selectAs(SceneEdificeEntity::getEdificeName, CarbonSinkDataResp::getSceneEdificeName)
                .end()
                .leftJoin(SceneFloorEntity.class, SceneFloorEntity::getId, CarbonSinkDataEntity::getSceneFloorId)
                .selectAs(SceneFloorEntity::getFloorName, CarbonSinkDataResp::getSceneFloorName)
                .end()
                .leftJoin(SceneRoomEntity.class, SceneRoomEntity::getId, CarbonSinkDataEntity::getSceneRoomId)
                .selectAs(SceneRoomEntity::getRoomName, CarbonSinkDataResp::getSceneRoomName)
                .end()
                .leftJoin(CarbonSinkFactorEntity.class, CarbonSinkFactorEntity::getId, CarbonSinkDataEntity::getCarbonSinkId)
                .selectAs(CarbonSinkFactorEntity::getType, CarbonSinkDataResp::getCarbonSinkName)
                .end()
                .eq(req.getSolidifyDate() != null, CarbonSinkDataEntity::getSolidifyDate, req.getSolidifyDate())
                .ge(StringUtils.isNotEmpty(req.getSolidifyDateStart()), CarbonSinkDataEntity::getSolidifyDate, req.getSolidifyDateStart())
                .le(StringUtils.isNotEmpty(req.getSolidifyDateEnd()), CarbonSinkDataEntity::getSolidifyDate, req.getSolidifyDateEnd())
                .eq(StringUtils.isNotEmpty(req.getSceneProjectId()), CarbonSinkDataEntity::getSceneProjectId, req.getSceneProjectId())
                .in(StringUtils.isNotEmpty(req.getSceneEdificeId()), CarbonSinkDataEntity::getSceneEdificeId, Arrays.asList(StringUtils.split(req.getSceneEdificeId(), ",")))
                .in(StringUtils.isNotEmpty(req.getSceneFloorId()), CarbonSinkDataEntity::getSceneFloorId, Arrays.asList(StringUtils.split(req.getSceneFloorId(), ",")))
                .in(StringUtils.isNotEmpty(req.getSceneRoomId()), CarbonSinkDataEntity::getSceneRoomId, Arrays.asList(StringUtils.split(req.getSceneRoomId(), ",")))
                .eq(StringUtils.isNotEmpty(req.getCarbonSinkId()), CarbonSinkDataEntity::getCarbonSinkId, req.getCarbonSinkId())
                .eq(StringUtils.isNotEmpty(req.getType()), CarbonSinkDataEntity::getType, req.getType())
                .in(CollectionUtils.isNotEmpty(req.getTypeList()), CarbonSinkDataEntity::getType, req.getTypeList())
                .eq(req.getArea() != null, CarbonSinkDataEntity::getArea, req.getArea())
                .eq(req.getCarbonEmission() != null, CarbonSinkDataEntity::getCarbonEmission, req.getCarbonEmission())
                .eq(StringUtils.isNotEmpty(req.getCarbonEmissionProcess()), CarbonSinkDataEntity::getCarbonEmissionProcess, req.getCarbonEmissionProcess())
                .eq(StringUtils.isNotEmpty(req.getDeptId()), CarbonSinkDataEntity::getDeptId, req.getDeptId())
                .in(CollectionUtils.isNotEmpty(req.getIds()), CarbonSinkDataEntity::getId, req.getIds())
                .ge(req.getBeginTime() != null, CarbonSinkDataEntity::getSolidifyDate, req.getBeginTime())
                .le(req.getEndTime() != null, CarbonSinkDataEntity::getSolidifyDate, req.getEndTime())
                .last(StringUtils.isNotEmpty(req.getSortColumn()), PageUtils.lastOrderBy(req))
                .orderByDesc(StringUtils.isEmpty(req.getSortColumn()), CarbonSinkDataEntity::getSolidifyDate);
    }

    @ApiOperation("详情")
    @PostMapping("detail")
    @RequiresPermissions("ecp-admin:carbonSinkData:search")
    public ResultResp<CarbonSinkDataResp> detail(@RequestBody @Validated(IRequired.class) BaseIdReq req) {
        return ResultResp.success(ConvertUtils.copy(carbonSinkDataService.getById(req.getId()), CarbonSinkDataResp.class));
    }

    /**
     * 组装查询条件
     *
     * @param req
     * @return
     */
    private static JoinLambdaWrapper<CarbonSinkDataEntity> buildQW(CarbonSinkDataQueryReq req) {
        String start = req.getSolidifyDateStart();
        String end = req.getSolidifyDateEnd();
        Date starDate = StringUtils.isEmpty(start) ? null : DateUtil.beginOfDay(DateUtil.parseDate(start));
        Date endDate = StringUtils.isEmpty(end) ? null : DateUtil.endOfDay(DateUtil.parseDate(end));
        return Joins.of(CarbonSinkDataEntity.class)
                .selectAll()
                .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, CarbonSinkDataEntity::getSceneProjectId)
                .select(SceneProjectEntity::getProjectName)
                .end()
                .leftJoin(SceneEdificeEntity.class, SceneEdificeEntity::getId, CarbonSinkDataEntity::getSceneEdificeId)
                .selectAs(SceneEdificeEntity::getEdificeName, CarbonSinkDataResp::getSceneEdificeName)
                .end()
                .leftJoin(SceneFloorEntity.class, SceneFloorEntity::getId, CarbonSinkDataEntity::getSceneFloorId)
                .selectAs(SceneFloorEntity::getFloorName, CarbonSinkDataResp::getSceneFloorName)
                .end()
                .leftJoin(SceneRoomEntity.class, SceneRoomEntity::getId, CarbonSinkDataEntity::getSceneRoomId)
                .selectAs(SceneRoomEntity::getRoomName, CarbonSinkDataResp::getSceneRoomName)
                .end()
                .leftJoin(CarbonSinkFactorEntity.class, CarbonSinkFactorEntity::getId, CarbonSinkDataEntity::getCarbonSinkId)
                .selectAs(CarbonSinkFactorEntity::getType, CarbonSinkDataResp::getCarbonSinkName)
                .end()
                .eq(req.getSolidifyDate() != null, CarbonSinkDataEntity::getSolidifyDate, req.getSolidifyDate())
                .ge(Objects.nonNull(starDate), CarbonSinkDataEntity::getSolidifyDate, starDate)
                .le(Objects.nonNull(endDate), CarbonSinkDataEntity::getSolidifyDate, endDate)
                .eq(StringUtils.isNotEmpty(req.getSceneProjectId()), CarbonSinkDataEntity::getSceneProjectId, req.getSceneProjectId())
                .eq(StringUtils.isNotEmpty(req.getSceneEdificeId()), CarbonSinkDataEntity::getSceneEdificeId, req.getSceneEdificeId())
                .eq(StringUtils.isNotEmpty(req.getSceneFloorId()), CarbonSinkDataEntity::getSceneFloorId, req.getSceneFloorId())
                .eq(StringUtils.isNotEmpty(req.getSceneRoomId()), CarbonSinkDataEntity::getSceneRoomId, req.getSceneRoomId())
                .eq(StringUtils.isNotEmpty(req.getCarbonSinkId()), CarbonSinkDataEntity::getCarbonSinkId, req.getCarbonSinkId())
                .eq(StringUtils.isNotEmpty(req.getType()), CarbonSinkDataEntity::getType, req.getType())
                .in(CollectionUtils.isNotEmpty(req.getTypeList()), CarbonSinkDataEntity::getType, req.getTypeList())
                .eq(req.getArea() != null, CarbonSinkDataEntity::getArea, req.getArea())
                .eq(req.getCarbonEmission() != null, CarbonSinkDataEntity::getCarbonEmission, req.getCarbonEmission())
                .eq(StringUtils.isNotEmpty(req.getCarbonEmissionProcess()), CarbonSinkDataEntity::getCarbonEmissionProcess, req.getCarbonEmissionProcess())
                .eq(StringUtils.isNotEmpty(req.getDeptId()), CarbonSinkDataEntity::getDeptId, req.getDeptId())
                .in(CollectionUtils.isNotEmpty(req.getIds()), CarbonSinkDataEntity::getId, req.getIds())
                .ge(req.getBeginTime() != null, CarbonSinkDataEntity::getSolidifyDate, Objects.isNull(req.getBeginTime()) ? null : DateUtil.beginOfDay(req.getBeginTime()))
                .le(req.getEndTime() != null, CarbonSinkDataEntity::getSolidifyDate, Objects.isNull(req.getEndTime()) ? null : DateUtil.endOfDay(req.getEndTime()))
                .last(StringUtils.isNotEmpty(req.getSortColumn()), PageUtils.lastOrderBy(req))
                .orderByDesc(StringUtils.isEmpty(req.getSortColumn()), CarbonSinkDataEntity::getSolidifyDate);
    }

    @ApiOperation("新增")
    @PostMapping("add")
    @RequiresPermissions("ecp-admin:carbonSinkData:add")
    public ResultResp<Boolean> add(@RequestBody @Validated({IAdd.class}) CarbonSinkDataReq req) {
        checkDataExists(req);
        CarbonSinkDataEntity entity = ConvertUtils.convertIgnoreId(req, CarbonSinkDataEntity.class);
        Pair<BigDecimal, String> result = CarbonSinkFactorHelper.i().calcDay(entity.getType(), entity.getSolidifyDate(), entity.getArea());
        entity.setCarbonEmission(result.getKey());
        entity.setCarbonEmissionProcess(result.getValue());
        return ResultResp.success(carbonSinkDataService.save(entity));
    }

    private void checkDataExists(CarbonSinkDataReq req) {
        Long count = carbonSinkDataService.lambdaQuery()
                .eq(CarbonSinkDataEntity::getSceneProjectId, req.getSceneProjectId())
                .eq(CarbonSinkDataEntity::getSolidifyDate, req.getSolidifyDate())
                .eq(CarbonSinkDataEntity::getType, req.getType())
                .ne(StringUtils.isNotEmpty(req.getId()), CarbonSinkDataEntity::getId, req.getId())
                .count();
        GrassException.throwEx(count > 0, "数据已存在！");
    }

    @ApiOperation("编辑")
    @PostMapping("update")
    @RequiresPermissions("ecp-admin:carbonSinkData:update")
    public ResultResp<Boolean> update(@RequestBody @Validated({IUpdate.class}) CarbonSinkDataReq req) {
        checkDataExists(req);
        CarbonSinkDataEntity entity = ConvertUtils.copy(req, CarbonSinkDataEntity.class);
        Pair<BigDecimal, String> result = CarbonSinkFactorHelper.i().calcDay(entity.getType(), entity.getSolidifyDate(), entity.getArea());
        entity.setCarbonEmission(result.getKey());
        entity.setCarbonEmissionProcess(result.getValue());
        return ResultResp.success(carbonSinkDataService.updateById(entity));
    }

    @ApiOperation("删除(根据Id删)")
    @PostMapping("deleteById")
    @RequiresPermissions("ecp-admin:carbonSinkData:deleteById")
    public ResultResp<Boolean> deleteById(@RequestBody @Validated({IRequired.class}) BaseIdReq req) {
        return ResultResp.success(carbonSinkDataService.removeById(req.getId()));
    }

    @ApiOperation("批量删除")
    @PostMapping("batchDelete")
    @RequiresPermissions("ecp-admin:carbonSinkData:batchDelete")
    public ResultResp<Boolean> batchDelete(@RequestBody @Validated({IRequired.class}) BaseBatchIdReq req) {
        return ResultResp.success(carbonSinkDataService.removeBatchByIds(req.getIds(), 1000));
    }

    @ApiOperation("导入")
    @PostMapping("upload")
    @RequiresPermissions("ecp-admin:carbonSinkData:upload")
    public ResultResp<Integer> upload(@RequestParam("file") MultipartFile file, Boolean ignoreError) {
        return ResultResp.success(ExcelParseManage.i().process(CarbonSinkDataRenderer.ENTITY_NAME, file, ignoreError));
    }

    @ApiOperation("导出")
    @PostMapping("export")
    @RequiresPermissions("ecp-admin:carbonSinkData:export")
    public void export(@RequestBody @Validated(IExport.class) CarbonSinkDataQueryReq req, HttpServletResponse response) {
        ExcelXSSFUtil.export("CarbonSinkDataExport", JsonUtil.toJson(buildQW(req).joinList(CarbonSinkDataResp.class), new ICarbonSinkDataFilter.CarbonSinkDataExportFilter()), response);
    }
}